package com.fancy.application.common.Interceptor;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.fancy.application.common.service.impl.SqlQueryWrapper;
import com.fancy.application.common.utils.SQLParseUtil;
import com.fancy.application.common.utils.UserUtil;
import com.fancy.application.sys.permission.entity.FilterInfo;
import com.fancy.application.sys.permission.entity.FilterRoleInfo;
import com.fancy.application.sys.permission.service.ISysPermissionService;
import lombok.extern.log4j.Log4j2;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.sql.Connection;
import java.util.List;
import java.util.Properties;

/**
 * 根据配置的文件来做数据权限
 * 管理员不做权限控制
 */
@Log4j2
@Component
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class SqlQueryInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        //通过MetaObject优雅访问对象的属性，这里是访问statementHandler的属性;：MetaObject是Mybatis提供的一个用于方便、
        //优雅访问对象属性的对象，通过它可以简化代码、不需要try/catch各种reflect异常，同时它支持对JavaBean、Collection、Map三种类型对象的操作。
        MetaObject metaObject = MetaObject
                .forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,
                        new DefaultReflectorFactory());
        //先拦截到RoutingStatementHandler，里面有个StatementHandler类型的delegate变量，其实现类是BaseStatementHandler，然后就到BaseStatementHandler的成员变量mappedStatement
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        String id = mappedStatement.getId();
        log.debug("方法全路径{}",id);
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
        log.debug("查询类型为：{}",sqlCommandType);
        //数据库连接信息
        if(!SqlCommandType.SELECT.equals(sqlCommandType)) return invocation.proceed();
        Object map =  statementHandler.getParameterHandler().getParameterObject();
        if(!(map instanceof MapperMethod.ParamMap)) return invocation.proceed();
        for (Object value : ((MapperMethod.ParamMap) map).values()) {
            if (value instanceof SqlQueryWrapper) {
                SqlQueryWrapper wrapper = (SqlQueryWrapper) value;
                Boolean flag = wrapper.getAuthQuery();
                if(flag){   //为true
                    if(UserUtil.isAdmin()) return invocation.proceed();
                    String sql = statementHandler.getBoundSql().getSql();
                    String clazzName = mappedStatement.getResultMaps().get(0).getType().getName();

                    log.debug("clazzName拦截器获取到的sql为=========:{}",clazzName);
                    FilterInfo filterInfo= getFilterInfo(clazzName);
                    String authSql = generateSqlByFilterInfo(filterInfo,sql);
                    log.debug("拦截器获取到的sql为=========:{}",sql);
                    log.debug("拦截器获取到的authSql为=========:{}",authSql);
                    metaObject.setValue("delegate.boundSql.sql", authSql);
                    break;
                }
            }
        }
        return invocation.proceed();
    }

    //根据权限过滤器来拼接sql
    private String generateSqlByFilterInfo(FilterInfo filterInfo, String sql) {
        String authSql = "";
        if(StrUtil.isNotEmpty(filterInfo.getAuthColumn())){
            authSql = generateSQLByIdFieldFilter(filterInfo.getAuthColumn(),sql);
        }else{
            authSql = sql;
            authSql = authSql +" where";
        }
        authSql = generateSQLByRoleFieldFilter(filterInfo.getAuthRole(),authSql);
        return authSql;
    }

    /**
     * 根据根据role生成sql
     * @return
     */
    private String generateSQLByRoleFieldFilter(List<FilterRoleInfo> authRole, String authSql) {
        String sql = "";
        for(FilterRoleInfo role:authRole){

            String tmpSql="select distinct p.fd_person_id from sys_permission_role_org_person p left join sys_permission_role_res_rel r on r.fd_role_id = p.fd_role_id where r.fd_res_id='"+role.getResName()+"'";
            if(!authSql.endsWith("where")){
                sql=sql+" or ";
            }
            sql = sql+" scope." + role.getKey() + " in ( " + tmpSql + " )";
        }
        authSql = authSql+sql;
        log.debug("role 生成的sql为：{}",authSql);
        return authSql;
    }

    /**
     * 根据idfilter生成sql
     * @return
     */
    private String generateSQLByIdFieldFilter(String authColumn,String sql) {
        List<String> columnList = SQLParseUtil.getColumns(sql);
        log.debug("解析出来的字段为：{}",columnList);
        String columnText = ArrayUtil.join(columnList.stream().map(str->"scope."+str).toArray(),", ");
        String fdId = UserUtil.getCurrentUser().getFdId();
        return "select "+columnText+" from ("+sql+") as scope where scope."+authColumn+" = '"+fdId+"'";
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

    /**
     * 获取数据权限过滤器的配置
     * @param modelName
     * @return
     */
    private FilterInfo getFilterInfo(String modelName){
        return sysPermissionService.getFilterInfoByModelName(modelName);

    }
    @Resource
    private ISysPermissionService sysPermissionService;
}
